#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <linux/usbdevice_fs.h>
#include <linux/usb/ch9.h>

#define LOG_TAG  "BW_RILD"
#include <log/log.h>

#include "utils.h"

s_usbdev_t udev;

int strStartsWith(const char *str, const char *match_str)
{
    for ( ; *str != '\0' && *match_str != '\0'; str++, match_str++) {
        if (*str != *match_str) {
            return 0;
        }
    }
    return *match_str == '\0';
}

static int get_usbsys_val(const char *sys_filename, int base)
{
    char buff[64] = {0};
    int ret_val = -1;

    int fd = open(sys_filename, O_RDONLY);
    if (fd < 0) {
        return -1;
    }

    if (read(fd, buff, sizeof(buff)) <= 0) {
        RLOGE("read:%s failed\n", sys_filename);
    }
    else {
        ret_val = strtoul(buff, NULL, base);
    }
    close(fd);

    return ret_val;
}

static int get_busname_by_uevent(const char *uevent, char *busname)
{
    FILE *fp = NULL;
    char line[BUF_SIZE] = {0};
    int MAJOR = 0, MINOR = 0;
    char DEVTYPE[64] = {0}, PRODUCT[64] = {0};

    fp = fopen(uevent, "r");
    if (fp == NULL) {
        RLOGE("fopen %s failed, errno:%d(%s)\n", uevent, errno, strerror(errno));
        return -1;
    }

    while (fgets(line, sizeof(line), fp))
    {
        if (line[strlen(line) - 1] == '\n' || line[strlen(line) - 1] == '\r') {
            line[strlen(line) - 1] = 0;
        }

        if (strStartsWith(line, "MAJOR="))
        {
            MAJOR = atoi(&line[strlen("MAJOR=")]);
        }
        else if (strStartsWith(line, "MINOR="))
        {
            MINOR = atoi(&line[strlen("MINOR=")]);
        }
        else if (strStartsWith(line, "DEVICE="))
        {
            strncpy(busname, &line[strlen("DEVICE=")], MAX_PATH_LEN);
        }
        else if (strStartsWith(line, "DEVNAME="))
        {
            strncpy(busname, &line[strlen("DEVNAME=")], MAX_PATH_LEN);
        }
        else if (strStartsWith(line, "DEVTYPE="))
        {
            strncpy(DEVTYPE, &line[strlen("DEVTYPE=")], sizeof(DEVTYPE));
        }
        else if (strStartsWith(line, "PRODUCT="))
        {
            strncpy(PRODUCT, &line[strlen("PRODUCT=")], sizeof(PRODUCT));
        }
    }
    fclose(fp);

    if (MAJOR != 189  || MINOR == 0 || busname[0] == 0
        || DEVTYPE[0] == 0 || PRODUCT[0] == 0
        || strStartsWith(DEVTYPE, "usb_device") == 0) {
        return -1;
    }

    return 0;
}


int usb_dev_open(int usb_vid, int usb_pid)
{
    char devdesc[MAX_PATH_LEN*2] = {0};
    char devname[MAX_PATH_LEN+128] = {0};
    size_t desc_length = 0, len = 0;
    int bInterfaceNumber = 0;

    DIR *usbdir = NULL;
    struct dirent *dent = NULL;
    int idVendor = 0, idProduct = 0;
    int bNumInterfaces = 0, bConfigurationValue = 0;
    char sys_filename[MAX_PATH_LEN] = {0};
    int ret = -1;

    usbdir = opendir("/sys/bus/usb/devices");
    if (usbdir == NULL) {
        return ret;
    }

    while ((dent = readdir(usbdir)) != NULL)
    {
        if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) {
            continue;
        }

        snprintf(sys_filename, sizeof(sys_filename), "%s/%s/idVendor", USB_DIR_BASE, dent->d_name);
        if ((idVendor = get_usbsys_val(sys_filename, 16)) <= 0) {
            continue;
        }

        snprintf(sys_filename, sizeof(sys_filename), "%s/%s/idProduct", USB_DIR_BASE, dent->d_name);
        if ((idProduct = get_usbsys_val(sys_filename, 16)) <= 0) {
            continue;
        }

        snprintf(sys_filename, sizeof(sys_filename), "%s/%s/bConfigurationValue", USB_DIR_BASE, dent->d_name);
        if ((bConfigurationValue = get_usbsys_val(sys_filename, 10)) <= 0) {
            continue;
        }

        snprintf(sys_filename, sizeof(sys_filename), "%s/%s/bNumInterfaces", USB_DIR_BASE, dent->d_name);
        if ((bNumInterfaces = get_usbsys_val(sys_filename, 10)) <= 0) {
            continue;
        }

        if((idVendor == usb_vid)&&(idProduct == usb_pid))
        {
            udev.idVendor = idVendor;
            udev.idProduct = idProduct;
            RLOGE("----------------------------------\n");
            RLOGE("idVendor: %04x\n", udev.idVendor);
            RLOGE("idProduct: %04x\n", udev.idProduct);
            RLOGE("bNumInterfaces: %d\n", bNumInterfaces);
            RLOGE("bConfigurationValue: %d\n", bConfigurationValue);
            snprintf(sys_filename, sizeof(sys_filename), "%s/%s/uevent", USB_DIR_BASE, dent->d_name);
            get_busname_by_uevent(sys_filename, udev.busname);
            RLOGE("busname: %s\n", udev.busname);
            RLOGE("----------------------------------\n");
	    ret = 0;
            break;
        }
        usleep(10000);
    }

    if (usbdir) {
        closedir(usbdir);
        usbdir = NULL;
    }


    snprintf(devname, sizeof(devname), "/dev/%s", udev.busname);

    if (access(devname, F_OK | R_OK| W_OK)) {
        RLOGE("access %s failed, errno:%d(%s)\n", devname, errno, strerror(errno));
        return ret;
    }

    udev.usbdev = open(devname, O_RDWR | O_NOCTTY);
    if (udev.usbdev < 0) {
        RLOGE("open %s failed, errno:%d(%s)\n", devname, errno, strerror(errno));
        return ret;
    }
    RLOGE("[%s] OK.\n", devname);

    desc_length = read(udev.usbdev, devdesc, sizeof(devdesc));
	RLOGE("desc_length is %zu,read length is %zu\r\n", sizeof(devdesc), desc_length);
    for (len=0; len<desc_length;)
    {
        struct usb_descriptor_header *h = (struct usb_descriptor_header *)(devdesc + len);

        if (h->bLength == sizeof(struct usb_device_descriptor) && h->bDescriptorType == USB_DT_DEVICE)
        {
             struct usb_device_descriptor *device = (struct usb_device_descriptor *)h;
             RLOGE("P: idVendor: %04x idProduct:%04x\n", device->idVendor, device->idProduct);
        }
        else if (h->bLength == sizeof(struct usb_config_descriptor) && h->bDescriptorType == USB_DT_CONFIG)
        {
             struct usb_config_descriptor *config = (struct usb_config_descriptor *)h;
             RLOGE("C: bNumInterfaces: %d\n", config->bNumInterfaces);
        }
        else if (h->bLength == sizeof(struct usb_interface_descriptor) && h->bDescriptorType == USB_DT_INTERFACE)
        {
            struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)h;

             RLOGE("I: If#= %d Alt= %d #EPs= %d Cls=%02x Sub=%02x Prot=%02x\n",
                 interface->bInterfaceNumber, interface->bAlternateSetting, interface->bNumEndpoints,
                 interface->bInterfaceClass, interface->bInterfaceSubClass, interface->bInterfaceProtocol);
            bInterfaceNumber = interface->bInterfaceNumber;
        }
        else if (h->bLength == USB_DT_ENDPOINT_SIZE && h->bDescriptorType == USB_DT_ENDPOINT)
        {
            struct usb_endpoint_descriptor *endpoint = (struct usb_endpoint_descriptor *)h;

                if ( (endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)
                {
                    if (endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
                        udev.bulk_ep_in = endpoint->bEndpointAddress;
                        RLOGE("bulk_ep_in:0x%02X\n", udev.bulk_ep_in);
                    } else {
                        udev.bulk_ep_out = endpoint->bEndpointAddress;
                        RLOGE("bulk_ep_out:0x%02X\n", udev.bulk_ep_out);
                    }
                    udev.wMaxPacketSize = endpoint->wMaxPacketSize;
                    RLOGE("wMaxPacketSize:%d\n", endpoint->wMaxPacketSize);
                }
        }
        len += h->bLength;
    }

    return 0;
}


